// Copyright 2022 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: provide query interfaces
// feature: develop dispatcher
// feature: discovery support client and server
// ------------------------------------------------------------------

#include <discovery/participant/DS/PDPSecurityInitiatorListener.hpp>

#include <mutex>

#include <edds/dds/core/policy/ParameterList.hpp>
#include <elog/Log.hpp>
#include <ertps/rtps/history/ReaderHistory.h>
#include <history/ParticipantListener.h>
#include <edds/rtps/reader/RTPSReader.h>

#include <edds/rtps/builtin/data/ParticipantProxyData.h>
#include <discovery/endpoint/EDP.h>
#include <discovery/participant/PDP.h>
#include <discovery/participant/PDPEndpoints.hpp>
#include <discovery/participant/PDPListener.h>
// #include <rtps/network/utils/external_locators.hpp>
#include <rtps/participant/RTPSParticipantImpl.h>
#include <edds/rtps/resources/TimedEvent.h>
#include <xmlparser/XMLParserPart.h>

using ParameterList = evbs::edds::dds::ParameterList;

namespace evbs {
namespace ertps {
namespace rtps {

PDPSecurityInitiatorListener::PDPSecurityInitiatorListener(PDP* parent, SecurityInitiatedCallback response_cb)
    : PDPListener(parent), response_cb_(std::move(response_cb)) {}

void PDPSecurityInitiatorListener::process_alive_data(ParticipantProxyData* old_data, ParticipantProxyData& new_data,
                                                      GUID_t& writer_guid, RTPSReader* reader,
                                                      std::unique_lock<std::recursive_mutex>& lock) {
    if (reader->matched_writer_is_matched(writer_guid)) {
        // Act as the standard PDPListener when the writer is matched.
        // This will be the case for unauthenticated participants when
        // allowed_unathenticated_participants is true
        PDPListener::process_alive_data(old_data, new_data, writer_guid, reader, lock);
        return;
    }
    // Re-authentication is required for the first discovery and when remote participant is restarted
    if ((old_data == nullptr) ||
        (old_data && old_data->m_startTime != 0 && old_data->m_startTime != new_data.m_startTime)) {
        if (old_data != nullptr) {
            parent_pdp_->mp_EDP->removeRemoteEndpoints(old_data);
            old_data->updateData(new_data);
            parent_pdp_->update_guid_startTime(new_data.m_guid.guidPrefix, new_data.m_startTime);
            parent_pdp_->mp_EDP->assignRemoteEndpoints(*old_data, true);
        }
        auto callback_data = new_data;
        reader->getMutex().unlock();
        lock.unlock();

#if HAVE_SECURITY
        //! notify security manager in order to start handshake
        const security::ParticipantSecurityAttributes& sec_attrs =
            parent_pdp_->getRTPSParticipant()->security_attributes();
        bool green_light = ((sec_attrs.mask() & ~PARTICIPANT_SECURITY_ATTRIBUTES_FLAG_IS_VALID) == 0);
        const bool permissive = !vbsutil::xmlparser::XMLParserpart::is_enforcing();
        green_light = green_light && permissive;  // No encryption and permissive

        bool ret = (parent_pdp_->getRTPSParticipant()->security_manager().discovered_participant(callback_data) ||
                    green_light);
        logDebug(SECURITY, "discovered_participant:" << callback_data.m_guid << " writer:" << writer_guid
                                                     << " reader:" << reader->getGuid() << " ret:" << ret);
        //! Reply to the remote participant
        if (ret) {
            response_cb_(callback_data);
        }
#endif
        // Take again the reader lock
        reader->getMutex().lock();
    }
}

bool PDPSecurityInitiatorListener::check_discovery_conditions(ParticipantProxyData& /* participant_data */) {
    /* Do not check PID_VENDOR_ID */
    // In Discovery Server we don't impose
    // domain ids to be the same
    /* Do not check PID_DOMAIN_ID */
    /* Do not check PARTICIPANT_TYPE */
    return true;
}

} /* namespace rtps */
} /* namespace ertps */
} /* namespace evbs */
